module share.types;

import std.math;
import std.traits;

public struct Point(T) if (isNumeric!(T))
{
    private T x = 0;
    private T y = 0;
    
    /**
     * @return The X coordinate of the Point.
     */
    public T getX() const pure nothrow
    {
        return x;
    }
    
    /**
     * @return The Y coordinate of the Point.
     */
    public T getY() const pure nothrow
    {
        return y;
    }
    
    /**
     * Sets the X coordinate of the Point.
     */
    public void setX(T x)
    {
        this.x = x;
    }
    
    /**
     * Sets the Y coordinate of the Point.
     */
    public void setY(T y)
    {
        this.y = y;
    }
    
    /**
     * Sets the X and Y coordinates of the Point.
     */
    public void set(T x, T y) pure nothrow
    {
        this.x = x;
        this.y = y;
    }
    
    /**
     * Constructor that sets the X and Y coordinates of the Point.
     */
    public this(T x, T y) pure nothrow
    {
        set(x, y);
    }
    
    /**
     * @return True if the two points have the same co-ordinates.
     * @since 0.1.0
     */
    public int opEquals(const ref Point!(T) rhs) const
    {
        return (rhs.getX() == x) &&
            (rhs.getY() == y);
    }
    
    public static immutable Point!(T) zero = Point!(T)(0, 0);
}

alias Point!(int) IntPoint;
alias Point!(float) FloatPoint;


public struct Size(T) if (isNumeric!(T))
{
    private T width = 0;
    private T height = 0;
    
    /**
     * @return The width.
     * @since 0.1.0
     */
    public T getWidth() pure nothrow
    {
        return width;
    }
    
    /**
     * @return The height.
     * @since 0.1.0
     */
    public T getHeight() pure nothrow
    {
        return height;
    }
    
    /**
     * Sets the width.
     * @param width The desired width.
     * @since 0.1.0
     */
    public void setWidth(T width)
    {
        this.width = width;
    }
    
    /**
     * Sets the height.
     * @param height The desired height.
     * @since 0.1.0
     */
    public void setHeight(T height)
    {
        this.height = height;
    }
    
    /**
     * Sets the width and height.
     * @param width The desired width.
     * @param height The desired height.
     * @since 0.1.0
     */
    public void set(T width, T height)
    {
        this.width = width;
        this.height = height;
    }
    
    /**
     * Constructs to the desired width and height.
     * @param width The desired width.
     * @param height The desired height.
     * @since 0.1.0
     */
    public this(T width, T height)
    {
        set(width, height);
    }
    
    public static immutable Size!(T) zero = Size!(T)(0, 0);
}

alias Size!(int) IntSize;
alias Size!(float) FloatSize;



public struct Rectangle(T) if (isNumeric!(T))
{
    private T left = 0;
    private T top = 0;
    private T right = 0;
    private T bottom = 0;
    
    /**
     * @return True if the point is inside the rectangle.
     * @since 0.1.0
     */
    public bool pointInside(in Point!(T) p) pure nothrow
    {
        if (p.getX() < left) return false;
        if (p.getY() < top) return false;
        if (p.getX() >= right) return false;
        if (p.getY() >= bottom) return false;
        return true;
    }
    
    /**
     * @return True if the width and height are 0.
     * @since 0.1.0
     */
    public bool isEmpty() const pure nothrow
    {
        //return width == 0 && height == 0;
        return (right <= left) || (bottom <= top);
    }


    /**
     * @return The left coordinate of the rectangle.
     * @since 0.1.0
     */
    public T getLeft() const pure nothrow
    {
        return left;
    }

    /**
     * @return The top coordinate of the rectangle.
     * @since 0.1.0
     */
    public T getTop() const pure nothrow
    {
        return top;
    }
    
    /**
     * @return The X coordinate + the width of the rectangle.
     * @since 0.1.0
     */
    public T getRight() const pure nothrow
    {
        return right;
    }
    
    /**
     * @return The Y coordinate + the height of the rectangle.
     * @since 0.1.0
     */
    public T getBottom() const pure nothrow
    {
        return bottom;
    }

    /**
     * @return The width of the rectangle.
     * @since 0.1.0
     */
    public T getWidth() const pure nothrow
    {
        return (right - left);
    }
    
    /**
     * @return The height of the rectangle.
     * @since 0.1.0
     */
    public T getHeight() const pure nothrow
    {
        return (bottom - top);
    }
    
    /**
     * @return The size of the rectangle.
     * @since 0.1.0
     */
    public Size!(T) getSize()
    {
        return Size!(T)(getWidth(), getHeight());
    }
    
    /**
     * @return A Rectangle made from TLBR.
     * @since 0.1.0
     */
    public static Rectangle!(T) fromLTRB(T left, T top,
                                     T right, T bottom)
    {
        return Rectangle!(T)(left, top, right, bottom);
    }
    
    /**
     * @return The top left Point.
     * @since 0.1.0
     */
    public Point!(T) getLeftTop() pure nothrow
    {
        return Point!(T)(getLeft(), getTop());
    }
    
    /**
     * @return The top right Point.
     * @since 0.1.0
     */
    public Point!(T) getTopRight() pure nothrow
    {
        return Point!(T)(getRight(), getTop());
    }
    
    /**
     * @return The bottom left Point.
     * @since 0.1.0
     */
    public Point!(T) getBottomLeft() pure nothrow
    {
        return Point!(T)(getLeft(), getBottom());
    }
    
    /**
     * @return The bottom right Point.
     * @since 0.1.0
     */
    public Point!(T) getRightBottom() pure nothrow
    {
        return Point!(T)(getRight(), getBottom());
    }

    /**
     * Sets the X coordinate of the rectangle.
     * @since 0.1.0
     */
    public void setLeft(T left) pure nothrow
    {
        this.left = left;
    }
    
    /**
     * Sets the Y coordinate of the rectangle.
     * @since 0.1.0
     */
    public void setTop(T top) pure nothrow
    {
        this.top = top;
    }
    
    /**
     * Sets the width of the rectangle.
     * @since 0.1.0
     */
    public void setRight(T right) pure nothrow
    {
        this.right = right;
    }
    
    /**
     * Sets the height of the rectangle.
     * @since 0.1.0
     */
    public void setBottom(T bottom) pure nothrow
    {
        this.bottom = bottom;
    }

    /**
     * @return The width of the rectangle.
     * @since 0.1.0
     */
    public void setWidth(T width) pure nothrow
    {
        right = left + width;
    }
    
    /**
     * @return The height of the rectangle.
     * @since 0.1.0
     */
    public void setHeight(T height) pure nothrow
    {
        bottom = top + height;
    }


    
    /**
     * Constructs a Rectangle with an x , y coordinate, a width, and height.
     * @since 0.1.0
     */
    public this(T left, T top, T right, T bottom)
    {
        set(left, top, right, bottom);
    }
    
    /**
     * Constructs a Rectangle with an Point and Dimension.
     * @since 0.1.0
     */
    public this(Point!(T) location, Size!(T) size)
    {
        setLeft(location.getX());
        setTop(location.getY());
        setWidth(size.getWidth());
        setHeight(size.getHeight());

        /*this.left = location.getX();
        this.top = location.getY();
        this.right = size.getWidth();
        this.bottom = size.getHeight();*/
    }

    public void set(T left, T top, T right, T bottom)
    {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }
    
    static immutable Rectangle!(T) zero = Rectangle!(T)(0, 0, 0, 0);
}

alias Rectangle!(int) IntRectangle;
alias Rectangle!(float) FloatRectangle;
alias Rectangle!(int) IntRect;
alias Rectangle!(float) FloatRect;